package com.leador.map.location.log;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Environment;
import android.os.IBinder;

import com.leador.map.location.Const;
import com.leador.map.location.util.Utils;

import org.apache.http.protocol.HTTP;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 */
public class Storage {
    private static Storage instance = null;
    private static Intent intentUp = null;
    private static ServiceConnection servConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
            Utils.writeCat("up serv connected");
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Utils.writeCat("up serv disconnected");
        }

    };

    private Storage() {
        //
    }

    /**
     * 获取STORAGEMANAGER实例
     *
     * @return StorageManager
     */
    public synchronized static Storage getInstance() {
        if (instance == null) {
            instance = new Storage();
        }
        return instance;
    }

    /**
     * 获取采集的临时/成果日志目录
     *
     * @param Context
     * @return String
     */
    public static String getLogDir(Context ctx) {
        File file = null;
        if (externalMemoryAvailable()) {
            file = Environment.getExternalStorageDirectory();
        } else {
            file = ctx.getCacheDir();
        }
        StringBuilder sb = new StringBuilder();
        sb.append(file.getAbsolutePath()).append("/");
        sb.append(Const.strLogDir).append("/");
        return sb.toString();
    }

    /**
     * 将日志进行ZIP压缩
     *
     * @param File
     * @param File
     * @return boolean
     */
    public static boolean zipAFile(File destZipFile, File srcFile) {
        if (destZipFile == null || !srcFile.isFile()) {
            return false;
        }
        boolean bResult = false;
        FileOutputStream fileOutputStream = null;
        ZipEntry zipEntry = null;
        ZipOutputStream zipOutputStream = null;
        FileInputStream fileInputStream = null;
        try {
            fileOutputStream = new FileOutputStream(destZipFile, true);
            zipEntry = new ZipEntry(srcFile.getName());
            zipOutputStream = new ZipOutputStream(fileOutputStream);
            zipOutputStream.putNextEntry(zipEntry);
            fileInputStream = new FileInputStream(srcFile);
            byte[] ba = new byte[1024 * 2];
            int iIndex = 0;
            while ((iIndex = fileInputStream.read(ba)) != -1) {
                zipOutputStream.write(ba, 0, iIndex);
            }
            ba = null;
            zipOutputStream.closeEntry();
            fileInputStream.close();
            zipOutputStream.close();
            fileOutputStream.close();
            bResult = true;
            Utils.writeCat("zip file success");
        } catch (Exception e) {
            Utils.printE(e);
            Utils.writeCat("zip file fail");
        } finally {
            fileOutputStream = null;
            zipEntry = null;
            zipOutputStream = null;
            fileInputStream = null;
        }
        return bResult;
    }

    /**
     * 判断日志是否需要ZIP压缩
     *
     * @param File
     * @return boolean
     */
    private static boolean logNeedZip(File file) {
        long lSize = file.length() / 1024l;
        Utils.writeCat("current log file size:", lSize, "kb");
        return (lSize > Const.iMaxTmpLogSize) ? true : false;
    }

    /**
     * 检查手机外存状态
     *
     * @param 无
     * @return boolean
     */
    public static boolean externalMemoryAvailable() {
        String strStatus = Environment.getExternalStorageState();
        return strStatus.equals(Environment.MEDIA_MOUNTED);
    }

    /**
     * 上传ZIP压缩日志
     *
     * @param ctx
     */
    public static void upZip(Context ctx) {
        if (!Const.bUp) {
            Utils.writeCat("up zip disabled");
            return;
        }
        Utils.writeCat("up zip");
        Context ctxTmp = ctx.getApplicationContext();
        if (intentUp == null) {
            intentUp = new Intent();
            intentUp.setClass(ctxTmp, UploadService.class);
        }
        if (ctxTmp.startService(intentUp) == null) {
            Utils.writeCat("up serv start fail");
            return;
        }
        if (ctxTmp.bindService(intentUp, servConn, Context.BIND_AUTO_CREATE)) {
            Utils.writeCat("up serv bind success");
        } else {
            Utils.writeCat("up serv bind fail");
        }
    }

    /**
     * 停止上传ZIP压缩日志
     *
     * @param Context
     * @return boolean
     */
    public static void stopUpZip(Context ctx) {
        if (intentUp == null) {
            return;
        }
        try {
            ctx.unbindService(servConn);
            Utils.writeCat("up un bind success");
        } catch (Exception e) {
            Utils.writeCat("up un bind fail");
        }
        if (ctx.stopService(intentUp)) {
            Utils.writeCat("up stop success");
        } else {
            Utils.writeCat("up stop fail");
        }
    }

    /**
     * 获取日志数据
     *
     * @param Log
     * @return byte[]
     */
    public byte[] getLogBa(Log log) {
        StringBuilder sbLog = new StringBuilder();
        sbLog.append(log.getCellTypeBaStr());
        sbLog.append(log.getImeiImsiMacBaStr());
        sbLog.append(log.getTimeBaStr());
        sbLog.append(log.getGpsBaStr());
        sbLog.append(log.getGsmBaStr());
        sbLog.append(log.getCdmaBaStr());
        sbLog.append(log.getWifiBaStr());
        sbLog.append(Log.ba2Str(Log.get1BFromInt(9)));
        String[] saLines = sbLog.toString().replaceAll(" + ", " ").split(" ");
        Utils.writeCat("log ba length is ", saLines.length);
        byte[] ba = new byte[saLines.length];
        for (int i = 0; i < saLines.length; i++) {
            try {
                ba[i] = Byte.parseByte(saLines[i]);
            } catch (Exception e) {
                Utils.writeCat("parse error in ba index ", (i + 1));
                Utils.printE(e);
                break;
            }
        }
        saLines = null;
        sbLog.delete(0, sbLog.length());
        sbLog = null;
        return logBaFine(ba) ? ba : null;
    }

    /**
     * 检查日志数据有效性
     *
     * @param byte[]
     * @return boolean
     */
    private boolean logBaFine(byte[] ba) {
        boolean bFine = true;
        if (ba == null) {
            bFine = false;
        } else if (ba[ba.length - 1] != 9) {
            bFine = false;
        } else if (ba[0] > 1 && ba[0] != Const.iDefCellType) {
            bFine = false;
        }
        return bFine;
    }

    /**
     * 写入调试信息
     *
     * @return 无
     */
    public synchronized void writeLog(String str) {
        if (!externalMemoryAvailable()) {
            return;
        }
        StringBuilder sb = new StringBuilder(200);
        sb.append(Environment.getExternalStorageDirectory().getAbsolutePath());
        sb.append("/");
        sb.append(Const.strLogDir).append("/");
        sb.append(Const.strDebugFile);
        String strDebugFileName = sb.toString();
        File file = new File(strDebugFileName);
        File fileDir = file.getParentFile();
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        DataOutputStream dos = null;
        FileOutputStream fos = null;
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            sb.delete(0, sb.length());
            sb.append(Log.formatUTC(Utils.getTime(), "yyyy-MM-dd HH:mm:ss"));
            sb.append("#");
            sb.append(str).append("\r\n");
            str = sb.toString();
            fos = new FileOutputStream(strDebugFileName, true);
            dos = new DataOutputStream(fos);
            dos.write(str.getBytes(HTTP.UTF_8));
            dos.flush();
            dos.close();
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            Utils.writeCat("permission WRITE_EXTERNAL_STORAGE denied", false);
        } catch (Exception e) {
            Utils.printE(e);
        } finally {
            dos = null;
            fos = null;
            str = null;
            file = null;
            fileDir = null;
        }
        sb.delete(0, sb.length());
        sb = null;
    }

    /**
     * 写入日志
     *
     * @param log
     * @param ctx
     */
    public synchronized void writeLog(Log log, Context ctx) {
        if (!Const.bSaveLog) {
            Utils.writeCat("write log disabled");
            return;
        }
        if (!Utils.coordInCN(log.gpsLon, log.gpsLat)) {
            Utils.writeCat("coord out of CN");
        }
        byte[] logBa = getLogBa(log);
        if (logBa == null) {
            Utils.writeCat("not write log, ba is null");
            return;
        }
        boolean bSaveTmpLog = true;
        StringBuilder sb = new StringBuilder();
        sb.append(getLogDir(ctx));
        sb.append(Const.strTmpLog);
        String strFileName = sb.toString();
        sb = null;
        File file = new File(strFileName);
        File fileDir = file.getParentFile();
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        if (logNeedZip(file)) {
            File fileNewZip = getNewZipFileIfAvailable(ctx);
            if (zipAFile(fileNewZip, file)) {
                bSaveTmpLog = true;
                file.delete();
            } else {
                bSaveTmpLog = false;
            }
        }
        DataOutputStream dataOutputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            if (bSaveTmpLog) {
                fileOutputStream = new FileOutputStream(strFileName, true);
                dataOutputStream = new DataOutputStream(fileOutputStream);
                dataOutputStream.write(logBa);
                dataOutputStream.write("\r\n".getBytes());
                dataOutputStream.flush();
                dataOutputStream.close();
                fileOutputStream.flush();
                fileOutputStream.close();
            }
        } catch (Exception e) {
            Utils.printE(e);
        } finally {
            dataOutputStream = null;
            fileOutputStream = null;
            logBa = null;
        }
        file = null;
        fileDir = null;
    }

    /**
     * 获取可供写入的ZIP日志
     *
     * @param Context
     * @return File
     */
    private File getNewZipFileIfAvailable(Context ctx) {
        int iIndex = 1;
        File fileHistoryZip = new File(getZipLogName(ctx, iIndex));
        while (fileHistoryZip.exists()) {
            iIndex++;
            fileHistoryZip = new File(getZipLogName(ctx, iIndex));
            if (iIndex > Const.iMaxZipLog) {
                fileHistoryZip = null;
                Utils.writeCat("reach max zip log file num");
                break;
            }
        }
        return fileHistoryZip;
    }

    /**
     * 获取ZIP压缩日志名称
     *
     * @param Context
     * @param int
     * @return String
     */
    private String getZipLogName(Context ctx, int iIndex) {
        return String.format("%s%s%s", getLogDir(ctx), Const.strZipLog, iIndex);
    }
}